#ifndef _CANARY2_SYS_TIMERBASE_H_
#define _CANARY2_SYS_TIMERBASE_H_

#include <set>
#include <mutex>
#include <chrono>
#include <concepts>
#include <algorithm>
#include <functional>
#include <variant>
#include <compare>
#include <ranges>

namespace wilson::sys
{
    using tt_timeout_point_t = 
        ::std::chrono::time_point<::std::chrono::system_clock>;
    using tt_callback_t = ::std::function<void()>;

    class timer_task
    {
    public:
        timer_task(tt_timeout_point_t tt_timeout_point, 
                   tt_callback_t callback);

        timer_task(const ::std::chrono::milliseconds& tt_timeout, 
                   tt_callback_t callback);

        const auto timeout_point() const noexcept 
        { 
            return tt_timeout_point; 
        }
        void do_callback();

        void operator()() { if (tt_callback) tt_callback(); }

    private:
        tt_timeout_point_t  tt_timeout_point;
        tt_callback_t       tt_callback;
    };

    ::std::strong_ordering 
    operator<=>(const timer_task& t1, const timer_task& t2) noexcept;

    class timerbase
    {
    public:
        timerbase() = default;
        timerbase(timerbase&& other);
        
        void add_task(timer_task tt);
        void add_task(::std::chrono::milliseconds timeout, 
                      ::std::function<void()> callback);

        void do_all_timeout();
        void operator()() { do_all_timeout(); }
        tt_timeout_point_t pioneer() const;
        bool done() const 
        { 
            ::std::lock_guard lock{ t_mutex }; 
            return t_tasks.empty(); 
        }

    private:
        template<typename BackInserter>
        void retrive_timeout_tasks(BackInserter beg)
        {
            if (t_tasks.empty()) return;
            timer_task dummy{ 
                ::std::chrono::system_clock::now(), 
                nullptr 
            };

            decltype(t_tasks)::iterator iter;
            for (iter = t_tasks.begin(); iter != t_tasks.end(); ++iter)
            {
                if (*iter > dummy) break;
            }

            // `iter` is the first element bigger than `dummy`
            if (iter == t_tasks.begin()) return;
            ::std::move(t_tasks.begin(), iter, beg);
            t_tasks.erase(t_tasks.begin(), iter);
        }

    private:
        ::std::multiset<timer_task> t_tasks;
        mutable ::std::mutex t_mutex;
    };
}

#endif
